{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "a02867dd",
   "metadata": {},
   "source": [
    "# 임베딩 기반 Evaluator(embedding_distance)\n",
    "\n",
    "답변과 정답 답변(reference) 사이의 거리를 측정하는 평가자를 생성합니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "34a30a84",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 설치\n",
    "# !pip install -U langsmith langchain-teddynote"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d75d1492",
   "metadata": {},
   "outputs": [],
   "source": [
    "# API KEY를 환경변수로 관리하기 위한 설정 파일\n",
    "from dotenv import load_dotenv\n",
    "\n",
    "# API KEY 정보로드\n",
    "load_dotenv()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "633d9db2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# LangSmith 추적을 설정합니다. https://smith.langchain.com\n",
    "# !pip install -qU langchain-teddynote\n",
    "from langchain_teddynote import logging\n",
    "\n",
    "# 프로젝트 이름을 입력합니다.\n",
    "logging.langsmith(\"CH16-Evaluations\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a09c8411",
   "metadata": {},
   "source": [
    "## RAG 성능 테스트를 위한 함수 정의"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "011f17eb",
   "metadata": {},
   "source": [
    "테스트에 활용할 RAG 시스템을 생성하겠습니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8a79916b",
   "metadata": {},
   "outputs": [],
   "source": [
    "from myrag import PDFRAG\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "# PDFRAG 객체 생성\n",
    "rag = PDFRAG(\n",
    "    \"data/SPRI_AI_Brief_2023년12월호_F.pdf\",\n",
    "    ChatOpenAI(model=\"gpt-4o-mini\", temperature=0),\n",
    ")\n",
    "\n",
    "# 검색기(retriever) 생성\n",
    "retriever = rag.create_retriever()\n",
    "\n",
    "# 체인(chain) 생성\n",
    "chain = rag.create_chain(retriever)\n",
    "\n",
    "# 질문에 대한 답변 생성\n",
    "chain.invoke(\"삼성전자가 자체 개발한 생성형 AI의 이름은 무엇인가요?\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "75b71e6e",
   "metadata": {},
   "source": [
    "`ask_question` 이라는 이름으로 함수를 생성합니다. 입력으로는 `inputs` 라는 딕셔너리를 받고, 출력으로는 `answer` 라는 딕셔너리를 반환합니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a922c6d7",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 질문에 대한 답변하는 함수를 생성\n",
    "def ask_question(inputs: dict):\n",
    "    return {\"answer\": chain.invoke(inputs[\"question\"])}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "90a10ca9",
   "metadata": {},
   "source": [
    "## 임베딩(Embedding) 거리 기반 Evaluator"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "722c4e24",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langsmith.evaluation import LangChainStringEvaluator\n",
    "from langchain_huggingface.embeddings import HuggingFaceEmbeddings\n",
    "from langchain_upstage import UpstageEmbeddings\n",
    "from langchain_openai import OpenAIEmbeddings\n",
    "import os\n",
    "\n",
    "# 토크나이저 병렬화 설정(HuggingFace 모델 사용)\n",
    "os.environ[\"TOKENIZERS_PARALLELISM\"] = \"true\"\n",
    "\n",
    "model_name = \"BAAI/bge-m3\"\n",
    "\n",
    "hf_embeddings = HuggingFaceEmbeddings(\n",
    "    model_name=model_name,\n",
    "    model_kwargs={\"device\": \"cpu\"},  # cuda, cpu\n",
    "    # encode_kwargs={\"normalize_embeddings\": True},\n",
    ")\n",
    "\n",
    "# 임베딩 모델 평가자 생성\n",
    "hf_embedding_evaluator = LangChainStringEvaluator(\n",
    "    \"embedding_distance\",\n",
    "    config={\n",
    "        # OpenAIEmbeddings 가 기본 값으로 설정되어 있으나, 변경이 가능\n",
    "        \"embeddings\": hf_embeddings,\n",
    "        \"distance_metric\": \"cosine\",  # \"cosine\", \"euclidean\", \"chebyshev\", \"hamming\", and \"manhattan\"\n",
    "    },\n",
    ")\n",
    "\n",
    "upstage_embedding_evaluator = LangChainStringEvaluator(\n",
    "    \"embedding_distance\",\n",
    "    config={\n",
    "        # OpenAIEmbeddings 가 기본 값으로 설정되어 있으나, 변경이 가능\n",
    "        \"embeddings\": UpstageEmbeddings(model=\"solar-embedding-1-large-query\"),\n",
    "        \"distance_metric\": \"euclidean\",  # \"cosine\", \"euclidean\", \"chebyshev\", \"hamming\", and \"manhattan\"\n",
    "    },\n",
    ")\n",
    "\n",
    "openai_embedding_evaluator = LangChainStringEvaluator(\n",
    "    \"embedding_distance\",\n",
    "    config={\n",
    "        # OpenAIEmbeddings 가 기본 값으로 설정되어 있으나, 변경이 가능\n",
    "        \"embeddings\": OpenAIEmbeddings(model=\"text-embedding-3-small\"),\n",
    "        \"distance_metric\": \"euclidean\",  # \"cosine\", \"euclidean\", \"chebyshev\", \"hamming\", and \"manhattan\"\n",
    "    },\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5ececbed",
   "metadata": {},
   "source": [
    "하나의 metric 에 여러개의 Embedding 모델이 사용되는 경우, 결과는 평균값으로 산정됩니다.\n",
    "\n",
    "(예시)\n",
    "- `cosine`: BGE-m3\n",
    "- `euclidean`: OpenAI, Upstage\n",
    "\n",
    "`euclidean` 의 경우, 각 모델의 평균 값이 산정됩니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bd511e37",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langsmith.evaluation import evaluate\n",
    "\n",
    "dataset_name = \"RAG_EVAL_DATASET\"\n",
    "\n",
    "# 평가 실행\n",
    "experiment_results = evaluate(\n",
    "    ask_question,\n",
    "    data=dataset_name,\n",
    "    evaluators=[\n",
    "        hf_embedding_evaluator,\n",
    "        upstage_embedding_evaluator,\n",
    "        openai_embedding_evaluator,\n",
    "    ],\n",
    "    experiment_prefix=\"EMBEDDING-EVAL\",\n",
    "    # 실험 메타데이터 지정\n",
    "    metadata={\n",
    "        \"variant\": \"embedding_distance 활용한 평가\",\n",
    "    },\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "db07e8e4",
   "metadata": {},
   "source": [
    "![](./assets/output-06.png)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "langchain-kr-lwwSZlnu-py3.11",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
